Option Explicit

'################################################################
' Discriptor
' 一维数据对象
'################################################################

Implements IDFSeries

'################################################################
' Field
'################################################################
' 数据缓存 (数组)
' 默认下标从 0 开始
' 初始化长度为 10
Private fieldData As Variant

' 保存数据的描述器
' Map<index, IDFDescriptor>
Private dataDescriptors As Object

' 类型处理器
Private dataTypeHandler As IDFDataTypeHandler

' 当前数组中保存的数据数量
Private fieldSize As Long

'################################################################
' Constructor
'################################################################
Private Sub Class_Initialize()
End Sub

Private Sub Class_Terminate()
    Set datadescriptors = Nothing
End Sub

'################################################################
' Init
'################################################################
' 初始化
' 下标从 1 开始创建缓存
' 缓存的默认初始化长度为 10
' 如果没有设置类型，则默认使用 String
'
' @optional-param data 需要缓存的实际数据 (数组类型)
' @optional-param size 数据缓存的初始化长度，默认长度 = 10
' @optional-param dType 数据类型
Public Sub Init(Optional ByRef data As Variant, Optional ByRef size As Long = 10, Optional ByRef dType As DF_DataType)
    ' 1. 如果手动传入了有效数据
    If IsArray(data) Then
        Dim dMin As Long
        Dim dMax As Long
        Dim dSize As Long
        dMin = LBound(data)
        dMax = UBound(data)
        dSize = dMax - dMin + 1

        ' 如果数据有长度，则进行数据类型检查与数据保存
        ' 如果没有长度（空数组），则向下从 0 开始创建
        If dSize > 0 Then
            ' 1.1 确认数据类型
            Dim i As Long

            If dType = 0 Then
                ' 如果没有提供数据类型，则使用第一个非 Empty 数据的类型，如果全部都是 Empty 类型，则默认使用 String 类型
                Dim firstType As DF_DataType
                Dim hasEffectiveData As Boolean

                hasEffectiveData = False
                For i = dMin To dMax
                    If IsEmpty(data(i)) = False Then
                        firstType = DFHelper.getDataTypeForPrimitiveData(data(i))
                        hasEffectiveData = True
                        Exit For
                    End If
                Next

                If hasEffectiveData = True Then
                    Set dataTypeHandler = DFHelper.getDataTypeHandler(firstType)
                Else
                    ' 如果全部都是 Empty 类型，则默认使用 String 类型
                    Set dataTypeHandler = DFHelper.getDataTypeHandler(DF_DataType.DF_STRING)
                End If
            Else
                ' 如果已经提供了数据类型，则直接使用
                Set dataTypeHandler = DFHelper.getDataTypeHandler(dType)
            End If

            ' 1.3 保存数据长度
            fieldSize = dSize

            ' 1.4 拷贝数据，并转换数据类型
            ' 1.4.1 设置内置缓存的下标从 0 开始
            ReDim fieldData(0 To (fieldSize - 1))
            ' 1.4.2 拷贝数据并转换类型
            For i = 0 To fieldSize - 1
                fieldData(i) = dataTypeHandler.normalize(data(dMin + i))
            Next

            ' 结束传入了数组数据的处理
            Exit Sub
        End If
    End If

    ' 2. 从 0 开始创建
    ' 如果没有设置数据，则需要进行检查
    ' 创建数据缓存
    If size <= 0 Then
        ' 初始化长度为 10
        ReDim fieldData(0 To 9)
    Else
        ReDim fieldData(0 To size - 1)
    End If

    ' 默认数据数量为 0
    fieldSize = 0

    ' 保存数据类型，如果没有设置类型，则默认使用 String
    If dType = 0 Then
        Set dataTypeHandler = DFHelper.getDataTypeHandler(DF_DataType.DF_STRING)
    Else
        Set dataTypeHandler = DFHelper.getDataTypeHandler(dType)
    End If

End Sub

'################################################################
' Property
'################################################################
' 获取当前一维数据对象中的数据类型
Public Property Get IDFSeries_DataType() As DF_DataType
    IDFSeries_DataType = dataTypeHandler.DataType
End Property

' 获取当前一维数据对象中的数据数量
Public Property Get IDFSeries_Size() As Long
    IDFSeries_Size = fieldSize
End Property

' 返回当前数据对象所使用的数据类型处理器
Public Property Get IDFSeries_TypeHandler() As IDFDataTypeHandler
    Set IDFSeries_TypeHandler = dataTypeHandler
End Property

'################################################################
' unsafe Function
' 内置加速方法，为了防止异常，在外部如果没有完善的检测机制不应该主动调用
'################################################################
' 内置取值方法
' 将不会对索引范围进行检查
Public Function IDFSeries_unsafeGetData(ByRef index As Long) As Variant
    IDFSeries_unsafeGetData = fieldData(index)
End Function

' 内置设置方法
' 将不会对索引范围进行检查
' 将不会对数据进行标准化处理
Public Sub IDFSeries_unsafeSetData(ByRef index As Long, ByRef val As Variant)
    fieldData(index) = val
End Sub

'################################################################
' Function
'################################################################
'--------------------------------------------------
' 缓存回收
'--------------------------------------------------
Public Sub IDFSeries_cacheCollect()
    ' 1. 计算缓存数组的最大索引
    Dim idxMax As Long
    idxMax = UBound(fieldData)

    ' 2. 如果最大索引比 fieldSize 大，则需要进行数组内存回收
    If idxMax >= fieldSize Then
        ReDim Preserve fieldData(0 To (fieldSize - 1))
    End If
End Sub

'--------------------------------------------------
' 数据读写
'--------------------------------------------------

' 检查索引是否在安全范围内
Public Function IDFSeries_hasIndex(ByRef index As Long) As Boolean
    If index < 0 Then
        IDFSeries_hasIndex = False
        Exit Function
    End If

    If index >= fieldSize Then
        IDFSeries_hasIndex = False
        Exit Function
    End If

    IDFSeries_hasIndex = True
End Function

' 获取指定索引位置的数据
' 如果指定位置没有数据，则默认返回 Empty
'
' 整型数字 DF_INT 保存成 Long 或 Integer 型
' 日期 DF_DATE 保存成字符串类型
' 时间 DF_TIME 保存成字符串类型
' 时间戳 DF_TIMESTAMP 保存成字符串类型
'
' @param index 数据索引
' @return 指定索引位置上的数据
Public Function IDFSeries_getData(ByRef index As Long) As Variant
On Error Goto catch:
    ' 不需要对数据做类型检查
    IDFSeries_getData = fieldData(index)
    Exit Function
catch:
    ' 处理索引范围异常，其他异常正常抛出
    If Err.Number = 9 Then
        Err.Raise 10110, Description:="parameter Index=" & index & " out of bounds. min index=0, max index[" & (fieldSize - 1) & "]"
    Else
        Err.Raise Err.Number, Description:=Err.Description
    End If
End Function

' 设置指定索引位置的数据
'
' @param index 数据索引
' @param val 需要设置的数据
Public Sub IDFSeries_setData(ByRef index As Long, ByRef val As Variant)
On Error Goto catch:
    fieldData(index) = dataTypeHandler.normalize(val)
    Exit Sub
catch:
    ' 处理索引范围异常，其他异常正常抛出
    If Err.Number = 9 Then
        Err.Raise 10110, Description:="parameter Index=" & index & " out of bounds. min index=0, max index[" & (fieldSize - 1) & "]"
    Else
        Err.Raise Err.Number, Description:=Err.Description
    End If
End Sub

'--------------------------------------------------
' CRUD
'--------------------------------------------------

' 向末尾新增一个数据
'
' @param val 需要设置的数据
Public Sub IDFSeries_appendData(ByRef val As Variant)
    ' 1. 检查数组范围
    '    如果数组已满，则进行扩容，每次扩容为原来的 1.5 倍
    If UBound(fieldData) <= (fieldSize - 1) Then
        ReDim Preserve fieldData(0 To (CLng(fieldSize * 1.5) - 1))
    End If

    ' 2. 保存数据
    fieldData(fieldSize) = dataTypeHandler.normalize(val)
    fieldSize = fieldSize + 1
End Sub

' 向末尾新增一个或多个空数据
'
' @param count 需要添加的空数据的数量，默认=1
Public Sub IDFSeries_appendEmpty(Optional ByRef count As Long = 1)
    ' 1. 计算缓存数组的剩余容量
    Dim usableSize As Long
    Dim cacheMax As Long
    cacheMax = UBound(fieldData)
    usableSize = cacheMax - fieldSize + 1

    ' 2. 如果新增容量 > 剩余容量，则需要进行扩容
    If count > usableSize Then
        ' 2.1 计算预设的扩容大小
        Dim newSize As Long
        newSize = CLng(fieldSize * 1.5)
        ' 2.2 如果扩容后的可用大小 <= 新增容量，则在此基础上在 + 10
        If (newSize - cacheMax - 1) <= count Then
            newSize = count + 10
        End If

        ' 2.3 进行扩容
        ReDim Preserve fieldData(0 To (newSize - 1))
    End If

    ' 2. 保存数据
    fieldSize = fieldSize + count
End Sub

' 删除指定索引位置的数据。只删除当前对象内的数据
' 该方法会导致内部数据产生移动，如果数据量很大，将会花费大量时间
'
' @param index 数据索引
Public Sub IDFSeries_delData(ParamArray indexes() As Variant)
    Dim ies As Variant
    ies = indexes
    IDFSeries_delDataByIndexArray ies
End Sub

' 删除指定索引位置的数据。只删除当前对象内的数据
' 该方法会导致内部数据产生移动，如果数据量很大，将会花费大量时间
'
' @param index 数据索引
Public Sub IDFSeries_delDataByIndexArray(ByRef indexes As Variant)
    ' 1. 如果没有有效参数，则立刻停止
    Dim idxMax As Long
    Dim idxMin As Long
    Dim delSize As Long
    idxMin = LBound(indexes)
    idxMax = UBound(indexes)
    delSize = idxMax - idxMin + 1
    If delSize = 0 Then
        Exit Sub
    End If

    ' 2. 检查所有索引的范围
    Dim i As Long
    Dim indexVal As Long
    For i = idxMin To idxMax
        indexVal = Int(indexes(i))
        If indexVal < 0 Or indexVal >= fieldSize Then
            Err.Raise 10110, Description:="parameter(" & i & ") Index=" & indexes(i) & " out of bounds. min index=0, max index[" & (fieldSize - 1) & "]"
            Exit Sub
        End If
    Next

    ' 3. 清除各索引上的数据
    For i = idxMin To idxMax
        indexVal = Int(indexes(i))

        Set fieldData(indexVal) = Nothing
    Next

    ' 4. 清理空槽位，并重新整理各索引对应的描述器
    DIm j As Long
    i = -1
    For j = 0 To (fieldSize - 1)
        If IsObject(fieldData(j)) <> True Then
            i = i + 1

            ' 如果是原地赋值，则跳过
            If i <> j Then
                fieldData(i) = fieldData(j)

                If Not dataDescriptors Is Nothing Then
                    If dataDescriptors.Exists(i) Then
                        dataDescriptors.Remove i
                        dataDescriptors.Add i, dataDescriptors.Item(j)
                    Else
                        dataDescriptors.Add i, dataDescriptors.Item(j)
                    End If
                End If
            End If
        End If
    Next

    ' 5. 移动数据后，清空 i -- fieldSize 的数据 + 描述器
    For j = i + 1 To (fieldSize - 1)
        fieldData(j) = Empty

        If Not dataDescriptors Is Nothing Then
            If dataDescriptors.Exists(j) Then
                dataDescriptors.Remove
            End If
        End If
    Next

    ' 6. 重新计算数据数量
    fieldSize = fieldSize - delSize
End Sub


' 删除指定索引位置的数据。如果当前是引用对象，也会直接删除原始对象中的数据
' 该方法会导致内部数据产生移动，如果数据量很大，将会花费大量时间
'
' @param index 数据索引
Public Sub IDFSeries_deepDelData(ParamArray indexes() As Variant)
    Dim ies As Variant
    ies = indexes
    IDFSeries_delDataByIndexArray ies
End Sub

' 删除指定索引位置的数据。如果当前是引用对象，也会直接删除原始对象中的数据
' 该方法会导致内部数据产生移动，如果数据量很大，将会花费大量时间
'
' @param index 数据索引
Public Sub IDFSeries_deepDelDataByIndexArray(ByRef indexes As Variant)
    IDFSeries_delDataByIndexArray indexes
End Sub

' 拷贝指定索引的数据到末尾
'
' @param index 数据索引
' @optional-param needDescriptor 如果数据存在描述符，是否需要拷贝，默认=False
Public Sub IDFSeries_copyAndAppend(ByRef index As Long, Optional ByRef needDescriptor As Boolean=False)
    ' 1. 拷贝数据
    IDFSeries_appendData IDFSeries_getData(index)

    ' 2. 拷贝描述符
    If needDescriptor = True Then
        ' 2.1 如果描述符不存在，则立刻结束
        If dataDescriptors Is Nothing Then
            Exit Sub
        End If

        If dataDescriptors.Exists(index) = False Then
            Exit Sub
        End If

        ' 2.2 拷贝描述符
        Dim d As IDFDescriptor
        Set d = dataDescriptors.Item(index)
        dataDescriptors.add (fieldSize - 1), d.clone
    End If
End Sub

' 使用指定数据填充内部所有数据是 Empty 的槽位
'
' 如果参数 val 无法被标准化为与当前 IDFSeries 类型相同的数据，将不会进行数据填充
'
' 对于切片类型，应该只填充引用的那些槽位，未引用的槽位不应该进行修改
'
' @param val 用来填充的数据
Public Sub IDFSeries_fillEmptySlot(ByRef val As Variant)
    ' 1. 标准化填充数据
    Dim fillVal As Variant
    fillVal = dataTypeHandler.normalize(val)

    ' 2. 如果标准化失败（参数 val 无法和当前 IDFSeries 的类型统一），则放弃填充操作
    If IsEmpty(fillVal) Then
        End Sub
    End If

    ' 3. 遍历整个对象，搜索 Empty 槽位，并填充数据
    For i = 0 To fieldSize - 1
        If IsEmpty(fieldData(i)) Then
            fieldData(i) = fillVal
        End IF
    Next
End Sub

'===========
' 基础类型计算
'===========
' 对所有数据进行指定方式的运算
'
' @param funcStr 运算函数名字符串
'        需要提供形式为 Function method(ByRef origin As Variant) As Variant 的方法
Public Sub IDFSeries_exec(ByRef funcStr As String)
    Dim i As Long
    For i = 0 To fieldSize - 1
        fieldData(i) = dataTypeHandler.normalize(Application.Run(funcStr, fieldData(i)))
    Next
End Sub

' 对所有数据进行指定方式的运算
'
' @param v 运算数据
' @param funcStr 运算函数名字符串
'        需要提供形式为 Function method(ByRef origin As Variant, ByRef computeVal As Variant) As Variant 的方法
Public Sub IDFSeries_compute(ByRef v As Variant, ByRef funcStr As String)
    Dim i As Long
    For i = 0 To fieldSize - 1
        fieldData(i) = dataTypeHandler.normalize( _
                        Application.Run(funcStr, fieldData(i), v) _
        )
    Next
End Sub

' 对所有数据进行加法运算
'
' 数值类型，会做加法
' 字符串，将会直接做字符串连接
' 日期字符串，将会按照天数进行相加
' 时间字符串，将会按照秒进行相加
' 时间戳字符串，将不会做处理，如果有需要请使用 compute 或 exec 方法
'
' @param v 运算数据
Public Sub IDFSeries_add(ByRef v As Variant)
    ' 1. 如果当前类型不支持该操作，则立刻停止
    If dataTypeHandler.SupportAdd = False Then
        Exit Sub
    End If

    ' 2. 执行计算
    dataTypeHandler.addForArray fieldData, fieldSize, v
End Sub

' 对所有数据进行减法运算
' 只支持数值型的数据，其他类型数据将会忽略
'
' @param v 运算数据
Public Sub IDFSeries_subtract(ByRef v As Variant)
    ' 1. 如果当前类型不支持该操作，则立刻停止
    If dataTypeHandler.SupportSubtract = False Then
        Exit Sub
    End If

    ' 2. 执行计算
    dataTypeHandler.subtractForArray fieldData, fieldSize, v
End Sub

' 对所有数据进行乘法运算
' 只支持数值型的数据，其他类型数据将会忽略
'
' @param v 运算数据
Public Sub IDFSeries_multiply(ByRef v As Variant)
    ' 1. 如果当前类型不支持该操作，则立刻停止
    If dataTypeHandler.SupportMultiply = False Then
        Exit Sub
    End If

    ' 2. 执行计算
    dataTypeHandler.multiplyForArray fieldData, fieldSize, v
End Sub

' 对所有数据进行除法运算
' 只支持数值型的数据，其他类型数据将会忽略
'
' @param v 运算数据
Public Sub IDFSeries_divide(ByRef v As Variant)
    ' 1. 如果当前类型不支持该操作，则立刻停止
    If dataTypeHandler.SupportDivide = False Then
        Exit Sub
    End If

    ' 2. 执行计算
    dataTypeHandler.divideForArray fieldData, fieldSize, v
End Sub

'===========
' 和其他 Series 类型做计算
'===========
' 和其他 Series 做加法运算，计算结果合并到当前 Series
'
' @param desc 运算 Series
Public Sub IDFSeries_mergeAdd(ByRef desc As Series)
    dataTypeHandler.mergeAdd Me, desc
End Sub

' 和其他 Series 做减法运算，计算结果合并到当前 Series
'
' @param desc 运算 Series
Public Sub IDFSeries_mergeSubtract(ByRef desc As Series)
    dataTypeHandler.mergeSubtract Me, desc
End Sub

' 和其他 Series 做乘法运算，计算结果合并到当前 Series
'
' @param desc 运算 Series
Public Sub IDFSeries_mergeMultiply(ByRef desc As Series)
    dataTypeHandler.mergeMultiply Me, desc
End Sub

' 和其他 Series 做除法运算，计算结果合并到当前 Series
'
' @param desc 运算 Series
Public Sub IDFSeries_mergeDivide(ByRef desc As Series)
    dataTypeHandler.mergeDivide Me, desc
End Sub

'=============
' 生成 Boolean IDFSeries
'=============
' 和指定运算符进行比较，返回一个和当前 series 相同的 Boolean 型的 series
' =
Public Function IDFSeries_eq(ByRef v As Variant) As IDFSeries
    Set IDFSeries_eq = dataTypeHandler.eqForArray(fieldData, fieldSize, v)
End Function

' !=
Public Function IDFSeries_ne(ByRef v As Variant) As IDFSeries
    Set IDFSeries_ne = dataTypeHandler.neForArray(fieldData, fieldSize, v)
End Function

' >
Public Function IDFSeries_gt(ByRef v As Variant) As IDFSeries
    Set IDFSeries_gt = dataTypeHandler.gtForArray(fieldData, fieldSize, v)
End Function

' <
Public Function IDFSeries_lt(ByRef v As Variant) As IDFSeries
    Set IDFSeries_lt = dataTypeHandler.ltForArray(fieldData, fieldSize, v)
End Function

' >=
Public Function IDFSeries_ge(ByRef v As Variant) As IDFSeries
    Set IDFSeries_ge = dataTypeHandler.geForArray(fieldData, fieldSize, v)
End Function

' <=
Public Function IDFSeries_le(ByRef v As Variant) As IDFSeries
    Set IDFSeries_le = dataTypeHandler.leForArray(fieldData, fieldSize, v)
End Function

' In()
Public Function IDFSeries_inVals(ByRef vs As Variant) As IDFSeries
    Set IDFSeries_inVals = dataTypeHandler.inValsForArray(fieldData, fieldSize, vs)
End Function

' between
Public Function IDFSeries_between(ByRef left As Variant, ByRef right As Variant) As IDFSeries
    Set IDFSeries_between = dataTypeHandler.betweenForArray(fieldData, fieldSize, left, right)
End Function

'=============
' 生成 Series ，保存所有结果为 true 的数据
'=============
' =
Public Function IDFSeries_eqToSeries(ByRef v As Variant) As IDFSeries
    Set IDFSeries_eqToSeries = dataTypeHandler.eqToSeriesForArray(fieldData, fieldSize, v)
End Function
' !=
Public Function IDFSeries_neToSeries(ByRef v As Variant) As IDFSeries
    Set IDFSeries_neToSeries = dataTypeHandler.neToSeriesForArray(fieldData, fieldSize, v)
End Function
' >
Public Function IDFSeries_gtToSeries(ByRef v As Variant) As IDFSeries
    Set IDFSeries_gtToSeries = dataTypeHandler.gtToSeriesForArray(fieldData, fieldSize, v)
End Function
' <
Public Function IDFSeries_ltToSeries(ByRef v As Variant) As IDFSeries
    Set IDFSeries_gtToSeries = dataTypeHandler.ltToSeriesForArray(fieldData, fieldSize, v)
End Function
' >=
Public Function IDFSeries_geToSeries(ByRef v As Variant) As IDFSeries
    Set IDFSeries_geToSeries = dataTypeHandler.geToSeriesForArray(fieldData, fieldSize, v)
End Function
' <=
Public Function IDFSeries_leToSeries(ByRef v As Variant) As IDFSeries
    Set IDFSeries_leToSeries = dataTypeHandler.leToSeriesForArray(fieldData, fieldSize, v)
End Function
' In()
Public Function IDFSeries_inValsToSeries(ByRef vs As Variant) As IDFSeries
    Set IDFSeries_inValsToSeries = dataTypeHandler.inValsToSeriesForArray(fieldData, fieldSize, vs)
End Function
' between
Public Function IDFSeries_betweenToSeries(ByRef left As Variant, ByRef right As Variant) As IDFSeries
    Set IDFSeries_betweenToSeries = dataTypeHandler.betweenToSeriesForArray(fieldData, fieldSize, left, right)
End Function

'=============
' 生成 Series 切片，保存所有结果为 true 的数据的引用
'=============
' =
Public Function IDFSeries_eqToSlice(ByRef v As Variant) As IDFSeries
    Set IDFSeries_eqToSlice = dataTypeHandler.eqToSliceForSeries(Me, v)
End Function
' !=
Public Function IDFSeries_neToSlice(ByRef v As Variant) As IDFSeries
    Set IDFSeries_neToSlice = dataTypeHandler.neToSliceForSeries(Me, v)
End Function
' >
Public Function IDFSeries_gtToSlice(ByRef v As Variant) As IDFSeries
    Set IDFSeries_gtToSlice = dataTypeHandler.gtToSliceForSeries(Me, v)
End Function
' <
Public Function IDFSeries_ltToSlice(ByRef v As Variant) As IDFSeries
    Set IDFSeries_ltToSlice = dataTypeHandler.ltToSliceForSeries(Me, v)
End Function
' >=
Public Function IDFSeries_geToSlice(ByRef v As Variant) As IDFSeries
    Set IDFSeries_geToSlice = dataTypeHandler.geToSliceForSeries(Me, v)
End Function
' <=
Public Function IDFSeries_leToSlice(ByRef v As Variant) As IDFSeries
    Set IDFSeries_leToSlice = dataTypeHandler.leToSliceForSeries(Me, v)
End Function
' In()
Public Function IDFSeries_inValsToSlice(ByRef vs As Variant) As IDFSeries
    Set IDFSeries_inValsToSlice = dataTypeHandler.inValsToSliceForSeries(Me, vs)
End Function
' between
Public Function IDFSeries_betweenToSlice(ByRef left As Variant, ByRef right As Variant) As IDFSeries
    Set IDFSeries_betweenToSlice = dataTypeHandler.betweenToSliceForSeries(Me, left, right)
End Function

'--------------------------------------------------
' 聚合运算
'--------------------------------------------------
' 返回 Series 中的最大值
' 如果当前 Series 的类型不支持 max 运算，则返回 Empty
Public Function IDFSeries_max() As Variant
    ' 1. 如果当前类型不支持该操作，则立刻停止
    If dataTypeHandler.SupportMax = False Then
        Exit Function
    End If

    ' 2. 执行计算
    IDFSeries_max = dataTypeHandler.maxForArray(fieldData, fieldSize)
End Function

' 返回 Series 中的最小值
' 如果当前 Series 的类型不支持 min 运算，则返回 Empty
Public Function IDFSeries_min() As Variant
    ' 1. 如果当前类型不支持该操作，则立刻停止
    If dataTypeHandler.SupportMax = False Then
        Exit Function
    End If

    ' 2. 执行计算
    IDFSeries_max = dataTypeHandler.minForArray(fieldData, fieldSize)
End Function

'--------------------------------------------------
' 数据描述符
'--------------------------------------------------

' 获取某个数据的描述器
' 如果不存在，则返回 Nothing
'
' @param index 数据索引
' @optional-param needCreate 如果描述信息对象不存在是否需要进行创建。默认值=False
' @return IDFDescriptor 数据描述器
Public Function IDFSeries_getDataDescriptor(ByRef index As Long, Optional ByRef needCreate As Boolean = False) As IDFDescriptor
    ' 1. 检查索引范围
    If index < 0 Or index >= fieldSize Then
        Err.Raise 10110, Description:="parameter Index=" & index & " out of bounds. min index=0, max index[" & (fieldSize - 1) & "]"
        Exit Function
    End If

    ' 2. 如果描述器缓存还未创建，并且允许创建描述器，则进行创建
    If dataDescriptors Is Nothing Then
        If needCreate = True Then
            ' dataDescriptors 创建缓存
            Set dataDescriptors = UtilsCreateSysObj.createMap
        Else
            ' 如果不允许创建，则立刻结束，并返回 Nothing
            Set IDFSeries_getDataDescriptor = Nothing
            Exit Function
        End If
    End If

    ' 3. 如果缓存中存在，则立刻返回
    If dataDescriptors.Exists(index) = True Then
        Set IDFSeries_getDataDescriptor = dataDescriptors.Item(index)
        Exit Function
    End If

    ' 4. 如果缓存中没有，并且允许创建描述器，则新建
    If needCreate = True Then
        ' 创建描述器并缓存
        Dim result As DFDescriptorInner
        Set result = New DFDescriptorInner
        dataDescriptors.Add index, result

        ' 结束
        Set IDFSeries_getDataDescriptor = result
    Else
        Set IDFSeries_getDataDescriptor = Nothing
    End If
End Function

'--------------------------------------------------
' 切片操作
'--------------------------------------------------
' 切片
' @optional-param start Long 起始位置
' @optional-param stop Long 终止位置(最终不会包含 stop 的索引)
' @optional-param step Long 步长
Public Function IDFSeries_slice(Optional startIdx As Variant, Optional ByRef stopIdx As Variant, Optional ByRef step As Variant=1) As IDFSeries
    Dim result As DFSeriesSlice
    Set result = New DFSeriesSlice
    result.Init Me, startIdx, stopIdx, step

    Set IDFSeries_slice = result
End Function

' 使用指定索引创建切片
' 如果数组长度为 0，则保留对所有数据的引用
'
' @param indexes 需要的真实索引
Public Function IDFSeries_sliceByIndexes(ParamArray indexes() As Variant) As IDFSeries
    Dim result As DFSeriesSlice
    Set result = New DFSeriesSlice

    Dim ies As Variant
    ies = indexes
    result.InitByIndexArray Me, ies

    Set IDFSeries_sliceByIndexes = result
End Function

' 使用指定索引数组创建切片
' 如果没有指定任何索引，则保留对所有数据的引用
'
' @param-array indexes 需要的真实索引
Public Function IDFSeries_sliceByIndexArray(ByRef indexes As Variant) As IDFSeries
    Dim result As DFSeriesSlice
    Set result = New DFSeriesSlice
    result.InitByIndexArray Me, indexes

    Set IDFSeries_sliceByIndexArray = result
End Function

'--------------------------------------------------
' 输出操作
'--------------------------------------------------

' 将 Series 中的内容输出到控制台
' 每个数据输出为一行
' Empty 将会以字符串 "@@EMPTY@@" 的形式输出
'
' @optional-param startIdx 输出的起始行(包括这一行，从 0 开始)
'                          只支持 0、正数，如果输入了一个负数，则自动使用 0
'                          如果 startIdx > endIdx，则不输出任何数据
' @optional-param endIdx 输出的结束行(包括这一行，从 0 开始)
'                          只支持 0、正数，如果输入了一个负数，则自动使用最大行索引
Public Sub IDFSeries_printToConsole(Optional ByRef startIdx As Long = 0, _
    Optional ByRef endIdx As Long = -1)

    ' 1. 如果当前 DF 没有任何行，则立刻结束
    If fieldSize = 0 Then
        Exit Sub
    End If

    ' 2. 确认参数
    Dim realStartIdx As Long
    Dim realEndIdx As Long
    If startIdx < 0 Then
        realStartIdx = 0
    Else
        If startIdx >= fieldSize Then
            realStartIdx = fieldSize - 1
        Else
            realStartIdx = startIdx
        End If
    End If
    If endIdx < 0 Then
        realEndIdx = fieldSize - 1
    Else
        If endIdx >= fieldSize Then
            realEndIdx = fieldSize - 1
        Else
            realEndIdx = endIdx
        End If
    End If

    ' 如果 startIdx > endIdx，则不输出任何数据
    If realStartIdx > realEndIdx Then
        Exit Sub
    End If

    ' 3. 按行输出内容
    Dim i As Long
    Dim temp As Variant
    For i = realStartIdx To realEndIdx
        temp = fieldData(i)

        If IsEmpty(temp) Then
            Debug.Print DFHelper.EMPTY_STR_IN_CONSOLE
        Else
            Debug.Print temp & ""
        End If
    Next
End Sub